#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include <linux/uinput.h>

#include "uinputux.h"

#define UINPUT_PATH  "/dev/uinput"

//events list
static const struct{ unsigned long request; int value; } uinputEventList[] = {
	
	//Relative axes
	{ UI_SET_EVBIT,  EV_REL     },
	{ UI_SET_RELBIT, REL_X      },
	{ UI_SET_RELBIT, REL_Y      },
	{ UI_SET_RELBIT, REL_WHEEL  },
	{ UI_SET_RELBIT, REL_HWHEEL },
	
	//mouse buttons 
	{ UI_SET_EVBIT,  EV_KEY     },
	{ UI_SET_KEYBIT, BTN_LEFT   },
	
	//arrow keys
	{ UI_SET_KEYBIT, KEY_LEFT   },
	{ UI_SET_KEYBIT, KEY_RIGHT  },
	{ UI_SET_KEYBIT, KEY_UP     },
	{ UI_SET_KEYBIT, KEY_DOWN   },
	
	{ 0, 0 }//list terminator
};

static inline int emitEvent( int ufd, int type, int code, int val ){
	
	struct input_event event = {
		.type = type,
		.code = code,
		.value = val,
		//timestamp values below are ignored
		.time.tv_sec = 0,
		.time.tv_usec = 0,
	};
	
	return write( ufd, &event, sizeof(event) );
}

/***************************************
func uinputEvent*
	handle data from scanner
input
	ufd - uinput file descriptor
	data - array of { tap, x, y }
***************************************/
//as touchpad
void uinputEventMouse( int ufd, short* data ){
	
	if( data[0] ){
		if( data[1] ) emitEvent( ufd, EV_REL, REL_X, -data[1] );
		if( data[2] ) emitEvent( ufd, EV_REL, REL_Y,  data[2] );
		emitEvent( ufd, EV_SYN, SYN_REPORT, 0 );
	}
}

//as mouse wheels for scroll
void uinputEventWheels( int ufd, short* data ){
	
	if( data[0] ){
		if( data[1] ) emitEvent( ufd, EV_REL, REL_HWHEEL, -data[1] );
		if( data[2] ) emitEvent( ufd, EV_REL, REL_WHEEL, data[2] > 0 ? -1 : 1 );
		emitEvent( ufd, EV_SYN, SYN_REPORT, 0 );
	}
}

//arrow keys
void uinputEventArrows( int ufd, short* data ){
	
	if( data[0] ){
		
		if( data[1] > 0  ){//left
			emitEvent( ufd, EV_KEY, KEY_LEFT,   1 );
			emitEvent( ufd, EV_SYN, SYN_REPORT, 0 );
			emitEvent( ufd, EV_KEY, KEY_LEFT,   0 );
		}else if( data[1] < 0  ){//right
			emitEvent( ufd, EV_KEY, KEY_RIGHT,  1 );
			emitEvent( ufd, EV_SYN, SYN_REPORT, 0 );
			emitEvent( ufd, EV_KEY, KEY_RIGHT,  0 );
		}
		
		if( data[2] < 0 ){//up
			emitEvent( ufd, EV_KEY, KEY_UP,  1 );
			emitEvent( ufd, EV_SYN, SYN_REPORT, 0 );
			emitEvent( ufd, EV_KEY, KEY_UP,  0 );
		}else if( data[2] > 0 ){//down
			emitEvent( ufd, EV_KEY, KEY_DOWN,  1 );
			emitEvent( ufd, EV_SYN, SYN_REPORT, 0 );
			emitEvent( ufd, EV_KEY, KEY_DOWN,  0 );
		}
		
		emitEvent( ufd, EV_SYN, SYN_REPORT, 0 );
	}
}

/**************************************
func uinputOpen
	Open uinput and create input device
return
	negative on error
	positive as uinput file descriptor
**************************************/
int uinputOpen( void ){
	
	int i;
	int ufd;
	struct uinput_setup usetup;
	int err;
	
	//open uinput
	ufd = open( UINPUT_PATH, O_WRONLY | O_NONBLOCK );
	if( ufd < 0 ){
		printf( "[uinput] Cannot open %s\n", UINPUT_PATH );
		return ufd;
	}
	
	//add events from list
	for( i = 0; uinputEventList[i].request; i++ ){
		err = ioctl( ufd, uinputEventList[i].request, uinputEventList[i].value );
		if( err < 0 ){
			printf( "[uinput] event [%i] setup error: %i\n", i, err );
			goto l_uinputClose;
		}
	}
	
	//create uinput device
	memset( &usetup, 0, sizeof(usetup) );
	usetup.id.bustype = BUS_USB;
	usetup.id.vendor  = 0x0483; //Upek
	usetup.id.product = 0x2016; //Fingerprint scanner
	strcpy( usetup.name, "Upek fingerprint scanner" );
	
	if( err >= 0 ) err = ioctl( ufd, UI_DEV_SETUP, &usetup );
	if( err >= 0 ) err = ioctl( ufd, UI_DEV_CREATE );
	
	if( err < 0 ){
		printf( "[uinput] create device error: %i\n", err );
		goto l_uinputClose;
	}
	
	return ufd;
	
l_uinputClose:
	close( ufd );
	return err;
}

/*************************************************
func uinputClose
	Destroy input device and close file descriptor
input
	ufd - uinput file descriptor to close
*************************************************/
void uinputClose( int ufd ){
	ioctl( ufd, UI_DEV_DESTROY );
	close( ufd );
}
